package com.swak.loadbalance.impl;

import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

import com.swak.loadbalance.Referer;

/**
 * 按权 - 最少使用数 ，感觉从统计来看和 不按权一致
 * 
 * @author lifeng
 * @date 2021年3月2日 下午5:45:16
 */
public class WeightingLeastActiveLoadBalance<T> extends AbstractLoadBalance<T> {

	@Override
	protected Referer<T> doSelect() {
		List<Referer<T>> referers = this.getReferers();
		// Number of invokers
		int length = referers.size();
		// The least active value of all invokers
		int leastActive = -1;
		// The number of invokers having the same least active value (leastActive)
		int leastCount = 0;
		// The index of invokers having the same least active value (leastActive)
		int[] leastIndexes = new int[length];
		// the weight of every invokers
		int[] weights = new int[length];
		// The sum of the warmup weights of all the least active invokers
		int totalWeight = 0;
		// The weight of the first least active invoker
		int firstWeight = 0;
		// Every least active invoker has the same weight value?
		boolean sameWeight = true;

		// Filter out all the least active invokers
		for (int i = 0; i < length; i++) {
			Referer<T> invoker = referers.get(i);
			// Get the active number of the invoker
			int active = invoker.getActiveCount();
			// Get the weight of the invoker's configuration. The default value is 100.
			int afterWarmup = invoker.getWeight();
			// save for later use
			weights[i] = afterWarmup;
			// If it is the first invoker or the active number of the invoker is less than
			// the current least active number
			if (leastActive == -1 || active < leastActive) {
				// Reset the active number of the current invoker to the least active number
				leastActive = active;
				// Reset the number of least active invokers
				leastCount = 1;
				// Put the first least active invoker first in leastIndexes
				leastIndexes[0] = i;
				// Reset totalWeight
				totalWeight = afterWarmup;
				// Record the weight the first least active invoker
				firstWeight = afterWarmup;
				// Each invoke has the same weight (only one invoker here)
				sameWeight = true;
				// If current invoker's active value equals with leaseActive, then accumulating.
			} else if (active == leastActive) {
				// Record the index of the least active invoker in leastIndexes order
				leastIndexes[leastCount++] = i;
				// Accumulate the total weight of the least active invoker
				totalWeight += afterWarmup;
				// If every invoker has the same weight?
				if (sameWeight && afterWarmup != firstWeight) {
					sameWeight = false;
				}
			}
		}
		// Choose an invoker from all the least active invokers
		if (leastCount == 1) {
			// If we got exactly one invoker having the least active value, return this
			// invoker directly.
			return referers.get(leastIndexes[0]);
		}
		if (!sameWeight && totalWeight > 0) {
			// If (not every invoker has the same weight & at least one invoker's weight>0),
			// select randomly based on
			// totalWeight.
			int offsetWeight = ThreadLocalRandom.current().nextInt(totalWeight);
			// Return a invoker based on the random value.
			for (int i = 0; i < leastCount; i++) {
				int leastIndex = leastIndexes[i];
				offsetWeight -= weights[leastIndex];
				if (offsetWeight < 0) {
					return referers.get(leastIndex);
				}
			}
		}
		// If all invokers have the same weight value or totalWeight=0, return evenly.
		return referers.get(leastIndexes[ThreadLocalRandom.current().nextInt(leastCount)]);
	}
}
